package com.chs.recoginition.core.utils;

import com.chs.recoginition.core.exception.constant.DateConstant;
import com.chs.recoginition.core.exception.ApplicationException;
import com.chs.recoginition.core.exception.constant.ExceptionConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class LocalDateTimeUtils {

    /**
     * 日期字符串转为日期对象
     */
    public static LocalDateTime parse(String dateStr, String pattern) {
        try {
            long timestamp = DateUtils.parseDate(dateStr, pattern).getTime();
            return parse(timestamp);
        } catch (ParseException e) {
            log.info("format string: {}", dateStr);
            throw new ApplicationException(ExceptionConstant.STR_FORMAT_FAIL);
        }
    }

    /**
     * 时间戳转为日期对象
     *
     * @param timestamp 毫秒时间戳
     */
    public static LocalDateTime parse(long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 日期字符串转为日期对象 多种情况匹配
     *
     * @param dateStr    日期字符串
     * @param patternArr 匹配的格式数组
     * @return 日期
     */
    public static LocalDateTime parseDateTime(String dateStr, String... patternArr) {
        LocalDateTime dateTime = null;
        log.info("Call the method parseDate(String,String..),and the dateStr is:{}", dateStr);
        for (String pattern : patternArr) {
            try {
                dateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
                if (dateTime != null) {
                    break;
                }
            } catch (DateTimeParseException ignored) {
            }
        }
        return dateTime;
    }

    /**
     * Date转LocalDate
     *
     * @param date Date对象
     */
    public static LocalDate dateToLocalDate(Date date) {
        if (date == null) {
            return null;
        }
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return instant.atZone(zone).toLocalDate();
    }

    /**
     * 日期字符串转为日期对象
     *
     * @param dateStr 日期字符串
     * @param pattern 匹配的格式
     * @return 日期
     */
    public static LocalDate parseDate(String dateStr, String pattern) {
        log.info("Call the method parseDate(String,String)");
        LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
        log.info("The method returns :{}", date);
        return date;
    }

    /**
     * 日期字符串转为日期对象 多种情况匹配
     *
     * @param dateStr    日期字符串
     * @param patternArr 匹配的格式数组
     * @return 日期
     */
    public static LocalDate parseDate(String dateStr, String... patternArr) {
        LocalDate date = null;
        log.info("Call the method parseDate(String,String..),and the dateStr is:{}", dateStr);
        for (String pattern : patternArr) {
            try {
                date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
                if (date != null) {
                    break;
                }
            } catch (DateTimeParseException ignored) {
            }
        }
        return date;
    }

    /**
     * 日期格式化
     */
    public static String format(LocalDateTime dateTime, String pattern) {
        return dateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期格式化
     */
    public static String format(LocalDate localDate, String pattern) {
        return localDate.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化日期
     */
    public static String format(Date date, String pattern) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        return simpleDateFormat.format(date);
    }

    /**
     * 获取两个日期之间的每日列表（包头包尾[开始日期，结束日期]）
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 每日列表，格式为yyyy-MM-dd
     */
    public static List<String> getDailyList(String startDate, String endDate) {
        return getDailyList(startDate, endDate, DateConstant.DATE_PATTERN_1);
    }

    /**
     * 获取两个日期之间的每日列表（包头包尾[开始日期，结束日期]）
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param pattern   格式，如 yyyy-MM-dd
     * @return 每日列表
     */
    public static List<String> getDailyList(String startDate, String endDate, String pattern) {
        LocalDateTime start = parse(startDate, pattern);
        LocalDateTime end = parse(endDate, pattern);

        List<String> dateStrList = new ArrayList<>();
        getDailyList(start, end).forEach(d -> dateStrList.add(format(d, pattern)));
        return dateStrList;
    }

    /**
     * 获取两个日期之间的每日列表（包头包尾[开始日期，结束日期]）
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 每日列表
     */
    public static List<LocalDateTime> getDailyList(LocalDateTime startDate, LocalDateTime endDate) {
        endDate = endDate.plusDays(1);
        return Stream.iterate(startDate, date -> date.plusDays(1))
                .limit(ChronoUnit.DAYS.between(startDate, endDate)).collect(Collectors.toList());
    }

    /**
     * 日期相隔天数
     */
    public static int periodDays(LocalDateTime start, LocalDateTime end) {
        long milliStart = start.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long milliEnd = end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        return (int) ((milliEnd - milliStart) / DateConstant.DAY);
    }


    /**
     * 两个日期是否同一天
     */
    public static boolean isSameDay(LocalDateTime dateTime1, LocalDateTime dateTime2) {
        return dateTime1.getYear() == dateTime2.getYear()
                && dateTime1.getMonthValue() == dateTime2.getMonthValue()
                && dateTime1.getDayOfMonth() == dateTime2.getDayOfMonth();
    }

    /**
     * 是否包含该日期，只判断是否同一天
     */
    public static boolean isContains(List<LocalDateTime> dateList, LocalDateTime date) {
        for (LocalDateTime ym : dateList) {
            if (LocalDateTimeUtils.isSameDay(date, ym)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取两个日期的中间时间点
     */
    public static Date getHalfDate(Date start, Date end) {
        return new Date((end.getTime() - start.getTime() >> 1) + start.getTime());
    }

    /**
     * LocalDate 转为 LocalDateTime
     */
    public static LocalDateTime convert(LocalDate localDate) {
        return LocalDateTime.of(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth(), 0, 0);
    }

    /**
     * LocalDate 转为 LocalDateTime
     */
    public static LocalDate convert(LocalDateTime localDateTime) {
        return LocalDate.of(localDateTime.getYear(), localDateTime.getMonthValue(), localDateTime.getDayOfMonth());
    }

    /**
     * LocalDateTime转换为Date
     *
     * @param localDateTime
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return null;
        }
        ZoneId zoneId = ZoneId.systemDefault();
        //Combines this date-time with a time-zone to create a  ZonedDateTime.
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    /**
     * LocalDate转换为Date
     */
    public static Date LocalDateToDate(LocalDate localDate) {
        if (localDate == null) {
            return null;
        }
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

}
